﻿namespace HuwmanCode.Runtime.InteropServices

open System
open System.Security
open System.Security.Permissions
open System.Runtime.InteropServices
open Microsoft.Win32.SafeHandles
open System.Runtime.ConstrainedExecution

[<RequireQualifiedAccess>]
module Marshal =
    type GlobalMemoryHandle(handle:nativeint, size:int) =
        do
            if handle <= IntPtr.Zero then failwith "Unable to allocate global memory!"

        member this.Size = size
        member this.Handle = handle

        static member create size handle =
            new GlobalMemoryHandle(handle, size)

        interface IDisposable with
            member x.Dispose() =
                Marshal.FreeHGlobal(handle)
   
    let sizeOf<'t> =
        Marshal.SizeOf(typeof<'t>)

    let allocGlobal (bytes:int) =
        Marshal.AllocHGlobal(bytes) |> GlobalMemoryHandle.create bytes

    let allocGlobal2<'t when 't : struct> =
        sizeOf<'t> * 1 |> allocGlobal

    let allocGlobal3<'t> instanceCount =
        sizeOf<'t> * instanceCount |> allocGlobal

    let structureToPtr (mem:GlobalMemoryHandle) (structure:'t when 't : struct) = 
        Marshal.StructureToPtr(structure, mem.Handle, false)

    let structuresToPtr (mem:GlobalMemoryHandle) (structures:'t array when 't : struct) =
        structures |> Array.iteri(fun idx a -> Marshal.StructureToPtr(a, mem.Handle + nativeint(idx * sizeOf<'t>), false))

    let packUnmanaged (structures:'t array when 't : struct) =
        let mem = allocGlobal3<'t> structures.Length
        structures |> structuresToPtr mem
        mem